package com.example.exercise;

/**
 * @author: dangwen
 * @createDate: 2021/6/17
 * 生产者消费者模型（wait/notify）
 */
public class ProducerAndConsumer01 {

    private static final int MAX_BULLET = 20;
    private static final int MIN_BULLET = 0;

    static class Gun{

        private int bullet_num = 0;

        /**
         * 不能再方法里写两层for循环，一定要在调用者方法里写一层for循环。
         * 原因是，synchronized加载方法上是类锁，一旦一个方法占用了类锁，除非这个方法执行完毕，其他方法时无法拿到锁的。
         * 这样就会出现有序的获得锁、释放锁的情况。而如果写在外层方法里，就会出现抢锁的情况，就有了交替执行的打印输出。
         */
        synchronized void input(){
//            while (true){
                while (bullet_num >= MAX_BULLET){
                    System.out.println("枪膛已满！");
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                bullet_num++;
                System.out.println("填入子弹，现在的子弹数量为：" + bullet_num);
                notifyAll();
//            }

        }

        synchronized void output() {
//            while (true){
                while (bullet_num <= MIN_BULLET){
                    System.out.println("枪膛已空！");
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                bullet_num--;
                System.out.println("射出子弹，现在的子弹数量为：" + bullet_num);
                notifyAll();
//            }
        }

    }

    public static class Producer implements Runnable{
        private Gun gun;

        public Producer(Gun gun){
            this.gun = gun;
        }

        @Override
        public void run() {
            while (true){
                gun.input();
            }
        }
    }

    public static class Consumer implements Runnable{

        private Gun gun;

        public Consumer(Gun gun){
            this.gun = gun;
        }

        @Override
        public void run() {
           while (true){
               gun.output();
           }
        }
    }

    public static void main(String[] args) {
        Gun gun = new Gun();
        Thread t1 = new Thread(new Producer(gun));
        t1.start();
        Thread t2 = new Thread(new Consumer(gun));
        t2.start();
    }

}
